package org.fjsei.yewu.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.PropertyFilter;
import io.netty.channel.ConnectTimeoutException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.fjsei.yewu.jyjc.JSONUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

public class Tool {
    static final LocalDate MIN_Year = LocalDate.of(0, 1, 1);
    static final LocalDate MAX_Year = LocalDate.of(9999, 12, 31);
    private static final PropertyFilter myJsonFilter =new PropertyFilter() {     //自定义JSON=精简存储；没内容的字符串，直接抛弃;
        public boolean apply(Object source, String name, Object value) {
            if(null==value)  return false;
            else if(value instanceof String)    return (StringUtils.hasText((String)value));
            else if(value instanceof Boolean)   return (Boolean)value;    //Boolean型false不生成
            else  return true;
        }
    };

    //public static <T> Collection<T> toCollection(Object iterableResult) {   }
    //@SuppressWarnings("unchecked")
    /**ID类型Long: String版本的;+ ID类型UUID版本的*/
    @Deprecated
    public static class ResolvedGlobalId {

        public ResolvedGlobalId(String type, String id) {
            this.type = type;
            this.id = id;
        }

        private final String type;
        //遗留： 实际应该Long id; 恰当;
        private final String id;

        public String getType() {
            return type;
        }

        public String getId() {
            return id;
        }
    }
    /**ID类型UUID或者Long兼容版本的*/
    public static class ResolvedGuuid {

        public ResolvedGuuid(String type, UUID id) {
            this.type = type;
            this.id = id;
        }
        /**依据type来判定实际的ID类型*/
        private final String type;
        /**Long类型也转换为UUID形式，发给前端的统一化可反向解析判定*/
        private final UUID id;   //变身UUID支持分布式DB

        public String getType() {
            return type;
        }
        /**ID是128位的UUID序列数*/
        public UUID getId() {
            return id;
        }
        /**ID是64位的整数*/
        public Long getLid() {
            return id.getMostSignificantBits();
        }
    }

    private static final java.util.Base64.Encoder encoder = java.util.Base64.getUrlEncoder().withoutPadding();
    private static final java.util.Base64.Decoder decoder = java.util.Base64.getUrlDecoder();
    @Deprecated
    public static String toGlobalId(String type, String id) {
        return encoder.encodeToString((type + ":" + id).getBytes(StandardCharsets.UTF_8));
    }
    public static String toGlobalId(String type, Long id) {
        return  toGlobalId(type, new UUID(id,0));
    }
    //URL安全的base64在encode后会这样处理：https://blog.csdn.net/peng314899581/article/details/100374826
    /**发送给前端的graphQL Global ID：自定义GlobalId 通用编码规则：要求前端可见唯一性。 node()直接获取type模型名称。
     *编码前: 前面16字节UUID,紧跟后面才是Type模型；发给前端Relay,graphQL要求的全局唯一性ID, 随后URI定位，前端不解析字节送回后端。
     *Base64； type byte[]， uuid就是long|long byte[]合并： 两个64位整数的；
     * 22个字符的uuid，用Base64转码的字符串, 不是直接bye to char: HEX展示的一般 ---- 36个字符串;
     * */
    public static String toGlobalId(String type, UUID id) {
        byte[] tpbyte=type.getBytes(StandardCharsets.UTF_8);
        long l= id.getMostSignificantBits();
        long s= id.getLeastSignificantBits();
        byte[] org = new byte[16+ tpbyte.length];
        org[7]=(byte)(l >>> 0);
        org[6]=(byte)(l >>> 8);
        org[5]=(byte)(l >>> 16);
        org[4]=(byte)(l >>> 24);
        org[3]=(byte)(l >>> 32);
        org[2]=(byte)(l >>> 40);
        org[1]=(byte)(l >>> 48);
        org[0]=(byte)(l >>> 56);
        //正好相反了：
        org[15]=(byte)(s >>> 0);
        org[14]=(byte)(s >>> 8);
        org[13]=(byte)(s >>> 16);
        org[12]=(byte)(s >>> 24);
        org[11]=(byte)(s >>> 32);
        org[10]=(byte)(s >>> 40);
        org[9]=(byte)(s >>> 48);
        org[8]=(byte)(s >>> 56);
        if(tpbyte.length>0)  System.arraycopy(tpbyte, 0, org, 16, tpbyte.length);
        return encoder.encodeToString(org);
    }
    public static ResolvedGlobalId fromGlobalId(String globalId) {
        String[] split = new String(decoder.decode(globalId), StandardCharsets.UTF_8).split(":", 2);
        if (split.length != 2) {
            throw new IllegalArgumentException(String.format("expecting a valid global id, got %s", globalId));
        }
        return new ResolvedGlobalId(split[0], split[1]);
    }
    /**UUID版本的: 从前端给出的ID来提取后端数据库id;前面16字节UUID紧跟后面才是Type
     * */
    public static ResolvedGuuid fromGluuId(String globalId) {
        byte[]  sou=decoder.decode(globalId);
        assert sou.length >= 16 : "guuid must be >=16 bytes";
        //ByteBuffer buffer = ByteBuffer.wrap(input,offset,8); .getLong();
        long msb = 0;
        long lsb = 0;
        for (int i=0; i<8; i++)
            msb = (msb << 8) | (sou[i] & 0xff);
        for (int i=8; i<16; i++)
            lsb = (lsb << 8) | (sou[i] & 0xff);
        if(sou.length>16) {
            byte[] typb = new byte[sou.length -16];
            System.arraycopy(sou, 16, typb, 0, sou.length-16);
            return new ResolvedGuuid(new String(typb, StandardCharsets.UTF_8), new UUID(msb, lsb));
        }
        else
            return new ResolvedGuuid("", new UUID(msb, lsb));
    }


    /**把字符串转成Float, 但是不抛出异常，主动删除非数字的前面的字符，强制转换。
     * 支持'-' 负数；
     * */
    public static Float castFloat(String strF) {
        if(!StringUtils.hasText(strF))   return null;
        int start=0;
        int end= strF.length();   //数字串的结束
        boolean  hasDot=false;     //遇见有了'.'吗;
        boolean begin=false;     //开始遇见数字了
        for (int i = 0; i < strF.length(); i++) {
            char ch= strF.charAt(i);
            if(begin && !(ch>='0' && ch<='9' || '.'==ch) ) {
                end = i;
                break;
            }
            if(ch>='0' && ch<='9') {
                if(!begin) {
                    begin = true;
                    if (i > 0 && '-' == strF.charAt(i - 1)) start = i - 1;
                    else start = i;
                }
            }
            else if('.'==ch){
                if(!hasDot)  hasDot=true;     //9.5.
                else {
                    end=i;
                    break;
                }
            }
        }
        //boolean isFloat= strF.matches("-?[0-9]]+.?[0-9]*]");
        if(begin){
            String subs= strF.substring(start, end);
            try {
                return  Float.valueOf(subs);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }

    /**把字符串转成Double, 但是不抛出异常，主动删除非数字的前面的字符，强制转换。
     * 支持'-' 负数；  前面和castFloat()都一样 ; Float精度较短6~7位啊, Double精度15~16位。
     * */
    public static Double castDouble(String strF) {
        if(!StringUtils.hasText(strF))   return null;
        int start=0;
        int end= strF.length();   //数字串的结束
        boolean  hasDot=false;     //遇见有了'.'吗;
        boolean begin=false;     //开始遇见数字了
        for (int i = 0; i < strF.length(); i++) {
            char ch= strF.charAt(i);
            if(begin && !(ch>='0' && ch<='9' || '.'==ch) ) {
                end = i;
                break;
            }
            if(ch>='0' && ch<='9') {
                if(!begin) {
                    begin = true;
                    if (i > 0 && '-' == strF.charAt(i - 1)) start = i - 1;
                    else start = i;
                }
            }
            else if('.'==ch){
                if(!hasDot)  hasDot=true;     //9.5.
                else {
                    end=i;
                    break;
                }
            }
        }
        //boolean isFloat= strF.matches("-?[0-9]]+.?[0-9]*]");
        if(begin){
            String subs= strF.substring(start, end);
            try {
                return  Double.valueOf(subs);
            } catch (NumberFormatException e) {
                return null;
            }
        }
        return null;
    }
    /**检查 从String 转换过来的数值 是否可能有问题。
     * 输入 gn 非空， strNumber非空。
     * */
    public static boolean testNumericalNormal(Double gn, String strNumber){
        String convv;
        long  numberV=gn.longValue();       //Long取值范围 gn=6.3E18;  后面乘^18个0;
        if(numberV==gn)  convv=String.valueOf(numberV);
        else  convv=gn.toString();      //整数情况 不能用：末尾加了 xxx.0
        String vols=strNumber.trim();
        //认定为可能不一致!很可能不合格有问题的数值
        return  convv.equals(vols);
    }

    /**自定义JSON生成器
     * */
    public static String  toJSONString(Object object) {
        return JSON.toJSONString(object, myJsonFilter);
    }
    /**根据排序好的数值，来匹配区间，返回点亮的区间索引
     * [特别注意！] divPam[A,b,C] 必须从小到大排列。 若value <=A返回0, 最大 若>C返回3; 返回值个数=divPam个数+1;
     * */
    public static int seqAreaCompare(double value,int [] divPam){
        int seq=0;
        int size=divPam.length;
        for (; seq<size; seq++) {
           if(divPam[seq] >= value)  break;
        }
        return  seq;
    }
    /**把对象倒腾存文件*/
    public static void writeObjectToFile(Object obj, String filename)
    {     //Debug模式运行file生成目录是： D:\\home\\unibackend 工程文件夹。
        File file =new File(filename);
        FileOutputStream out;
        try {
            out = new FileOutputStream(file);
            ObjectOutputStream objOut=new ObjectOutputStream(out);
            objOut.writeObject(obj);
            objOut.flush();
            objOut.close();
            System.out.println("write object success!");
        } catch (IOException e) {
            System.out.println("write object failed");  //obj 务必要 implements Serializable
            e.printStackTrace();
        }
    }
    /**把存文件对象还原到内存*/
    public static Object readOjbectFromFile(String filename)
    {
        Object temp=null;
        File file =new File(filename);
        FileInputStream in;
        try {
            in = new FileInputStream(file);
            ObjectInputStream objIn=new ObjectInputStream(in);
            temp=objIn.readObject();
            objIn.close();
            System.out.println("read object success!");
        } catch (IOException e) {       //java.io.FileNotFoundException:
            System.out.println("read object failed 文件没有? "+filename);
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return temp;
    }

    /**
     * 反射机制来获取单个对象指定键的值 ReflectObjectUtil
     * @param t
     * @param key 属性字段名字
     * @param <T> 对象类型
     * @return 实际类的字段类型定义型
     */
    public static <T> Object reflectGetValueByKey(T t, String key) {
        Class clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Field resultField = Arrays.stream(fields)
                .filter(field -> field.getName().equals(key))
                .findFirst()
                .get();
        //没有该字段 .get()报错的;
        Object obj = null;
        resultField.setAccessible(true);
        try {
            obj = resultField.get(t);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return obj;
    }
    /**从排序的序列中 搜索2个差值在预定义+-区间的范围，返回范围的顺序索引 4个序列值。  差值类型=Integer;
     * 函数值sortcmpFunc(i： 0...sortMax): 【务必确保】必须是从小到大的输出。
     * 最大下标sortMax: -1 的情况，说明排序队列为空是返回[int]四个数[-1,-1,-1,-1]。[大值起始,小值起始,小值终点,大值终点]
     *返回[]大值起始，小值起始:  若是返回=-1的，表示该相应区间没遇见有效取值的情况， 不是-1的话相应的值终点才有意义的。
     * */
    public static int[] sortListMatchTwoYz(int sortMax, Function<Integer, Integer>  sortcmpFunc, int firstYz, int secondYz) {
        Assert.isTrue(firstYz<=secondYz && firstYz>=0, "无效大小数区间");
        //合理天数：管道60 定期180 eqpEs.getType().equals("8")? 60 : 180; 不可能这么短暂天数：来自动生成两次的任务单
        int guessofs1= -1, guessofs2= -1;
        int guessend1= sortMax, guessend2= sortMax;     //指针；
        for (int i = 0; i <= sortMax; i++) {         //准备好 60 : 180 天范围之内任务列表集合:两个范围区。
            int differ= sortcmpFunc.apply(i);
            if(differ>= -firstYz  &&  -1==guessofs1)   guessofs1=i;
            if(differ>= -secondYz  &&  -1==guessofs2)   guessofs2=i;
            if(differ> firstYz && sortMax==guessend1)   guessend1=i-1;
            if(differ> secondYz && sortMax==guessend2)   guessend2=i-1;
        }
        if(guessofs1 > guessend1)   guessofs1= -1;      //表示小数值1区间 没有有效值
        if(guessofs2 > guessend2)   guessofs2= -1;      //表示大数值2区间 没有有效值
        return new int[]{guessofs2, guessofs1, guessend1, guessend2};
    }

    /**因为org.springframework.data.elasticsearch.core.query.BaseQuery#setSearchAfter(java.util.List)处理问题导致，需要转换日期。
     *@param dateStrLong 是ES搜索searchHits = esTemplate.search(query, type)获取的 xxHit.getSortValues()[日期字段]获得的日期long表达的。
     * @return 转换为LocalDate日期，送给 spring-data-ES搜索当前支持的要求字符串表示的排序字段。 with format [uuuu-MM-dd]
     * */
    public static LocalDate  longstr2Date(String dateStrLong) {
        Instant instant=Instant.ofEpochMilli(Long.parseLong(dateStrLong));
        LocalDate date=LocalDate.ofInstant(instant, ZoneId.of("Asia/Shanghai"));
        if(date.getYear()<0)    date= MIN_Year;        //针对Order.desc("nxtd1")并且nxtd1==null场景的；
        if(date.getYear()>=10000)    date= MAX_Year;        //4位数年份，ES8转换[uuuu-MM-dd]支持。 针对.asc()是正数无限大日期。
        return date;
    }
    /**把对象做toString若null的情况返回是NULL*/
    public static String toString(Object any)
    {
        return  (null!=any)? any.toString() : null;
        //return (null==any)? "" : any.toString();  前端没必要这个""  ？浪费存储空间
    }
    /**把String若为""或仅仅含空格的情况返回是null*/
    public static String String(String any)
    {
        if(StringUtils.hasText(any))   return any;
        else return null;
    }
    /**浮点数转字符串*/
    public static String F2String(Float floatValue) {
        if(null==floatValue)    return null;
        // 判断是否为整数
        if ((floatValue % 1) == 0) {
            return String.valueOf((int) floatValue.intValue());
        } else {
            // 如果是小数，转换为带有小数点的字符串
            return String.valueOf(floatValue);
        }
    }
    /**Short整数转字符串*/
    public static String St2String(Short stValue) {
        if(null==stValue)    return null;
        else return String.valueOf(stValue);
    }

    public static Integer String2Int(String stValue) {
        if (null == stValue) return null;
        try {
            // 使用正则表达式去除非数字字符 比如"3000 kg"
            String cleanedInput = stValue.replaceAll("\\D+", "");
            // 尝试将清理后的字符串转换为整数
            return Integer.parseInt(cleanedInput);
        } catch (NumberFormatException e) {
            // 如果转换失败，则捕获异常并返回null
                    System.err.println("Errorinteger: " + e.getMessage());
            return null;
        }
    }
    public static Double String2Double(String stValue) {
        if(null==stValue)    return null;
        else return Double.valueOf(stValue);
    }
    public static LocalDate String2Date(String stValue) {
        if(null==stValue)    return null;
        else return LocalDate.parse(stValue);
    }
    public static Short String2Short(String stValue) {
        if(null==stValue)    return null;
        try {
            // 使用正则表达式去除非数字字符 比如"3000 kg"
            String cleanedInput = stValue.replaceAll("\\D+", "");
            // 尝试将清理后的字符串转换为整数
            return Short.parseShort(cleanedInput);
        } catch (NumberFormatException e) {
            // 如果转换失败，则捕获异常并返回null
            System.err.println("Errorshort: " + e.getMessage());
            return null;
        }
    }
}
